/* BSD Socket API Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "nvs_flash.h"

#include "esp_console.h"
#include "argtable3/argtable3.h"


#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>

#include "gw-device.h"

#include "internet_common.h"






static const int CONNECTED_BIT = IPV4_GOTIP_BIT | IPV6_GOTIP_BIT;

static const char *TAG = "WIFI-APP";





static void initialise_wifi(void)
{
    // static bool initialized = false;
    // if (initialized) {
    //     return;
    // }

    esp_log_level_set("wifi", ESP_LOG_WARN);

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
    ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_NULL) );
    ESP_ERROR_CHECK( esp_wifi_start() );

    // initialized = true;
}

static void deinit_wifi(void)
{
    esp_wifi_disconnect();
    esp_wifi_stop();
}

//false:connect fail true:connect success
static bool wifi_join(const char* ssid, const char* pass, int timeout_ms)
{
    ESP_LOGI(TAG,"ssid:%s,password:%s",ssid,pass);
    initialise_wifi();
    wifi_config_t wifi_config = { 0 };
    strncpy((char*) wifi_config.sta.ssid, ssid, strlen(ssid));
    if (pass) {
        strncpy((char*) wifi_config.sta.password, pass, strlen(pass));
    }

    // if(isWIfiConnected() == true)
    //     ESP_ERROR_CHECK( esp_wifi_disconnect() );

    // ESP_ERROR_CHECK( esp_wifi_disconnect() );
    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
    ESP_ERROR_CHECK( esp_wifi_connect() );

    xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,1, 1, timeout_ms / portTICK_PERIOD_MS);
    return isWIfiConnected();
}


/** Arguments used by 'join' function */
static struct {
    struct arg_int *timeout;
    struct arg_str *ssid;
    struct arg_str *password;
    struct arg_end *end;
} join_args;

static int wifi_connect(int argc, char** argv)
{
    int nerrors = arg_parse(argc, argv, (void**) &join_args);
    if (nerrors != 0) {
        arg_print_errors(stderr, join_args.end, argv[0]);
        return 1;
    }
    ESP_LOGI(__func__, "Connecting to '%s'",join_args.ssid->sval[0]);

    deinit_wifi();
    //确保SSID输入有效
    if(strlen(join_args.ssid->sval[0])==0)
    {
        printf("please input valid string(Chinese does not support)\n");
        return -1;
    }
    if(strlen(join_args.ssid->sval[0]) > 32 || strlen(join_args.password->sval[0]) > 32)
    {
        printf("ssid or password too long(>32)\n");
        return -1;
    }

    bool connected = wifi_join(join_args.ssid->sval[0],
                           join_args.password->sval[0],
                           join_args.timeout->ival[0]);

    config_para_t* temp_para = (config_para_t*)malloc(sizeof(config_para_t));

    if(temp_para == NULL)
    {
        ESP_LOGE("set","malloc failed");
        ESP_LOGE("set","device set failed");
        return -1;
    }

    get_device_settings(temp_para);

    if (!connected) {
        ESP_LOGW(__func__, "Connection timed out");

        wifi_config_t wifi_config = { 0 };
        strncpy((char*) wifi_config.sta.ssid, (char*)temp_para->wifi_info.ssid, sizeof(wifi_config.sta.ssid));
        strncpy((char*) wifi_config.sta.password, (char*)temp_para->wifi_info.password, sizeof(wifi_config.sta.password));

        //恢复之前的wifi设置
        ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
        ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );

        printf("connect failed,set the ssid & password as before\n");

        free(temp_para);
        return 1;
    }
    else
    {
        memset(temp_para->wifi_info.ssid,0,sizeof(temp_para->wifi_info.ssid));
        memset(temp_para->wifi_info.password,0,sizeof(temp_para->wifi_info.password));

        memcpy(temp_para->wifi_info.ssid,join_args.ssid->sval[0],strlen(join_args.ssid->sval[0]));
        memcpy(temp_para->wifi_info.password,join_args.password->sval[0],strlen(join_args.password->sval[0]));
        temp_para->wifi_info.wifi_timeout = join_args.timeout->ival[0];

        set_device_settings(temp_para);
        save_device_settings();

        ESP_LOGI(__func__, "Connected:ssid,%s,password%s",join_args.ssid->sval[0],join_args.password->sval[0]);
    
        printf("connect successed,set the ssid & password as the input\n");
        printf("you must 'reboot' the device to take effect the parameters\n");
    }

    free(temp_para);

    return 0;
}

void register_wifi()
{
    join_args.timeout = arg_int0("t", "timeout", "<t>", "Connection timeout, ms");
    join_args.timeout->ival[0] = 10000; // set default value
    join_args.ssid = arg_str1(NULL, NULL, "<ssid>", "SSID of AP");
    join_args.password = arg_str0(NULL, NULL, "<pass>", "PSK of AP");
    join_args.end = arg_end(2);

    const esp_console_cmd_t join_cmd = {
        .command = "join",
        .help = "Join WiFi AP as a station",
        .hint = NULL,
        .func = &wifi_connect,
        .argtable = &join_args
    };

    ESP_ERROR_CHECK( esp_console_cmd_register(&join_cmd) );
}

// int wifi_init(char* ssid,char* password,int timeout)
int wifi_init(void* args)
{
    wifi_info_t* wifi_info = (wifi_info_t*)args;
    ESP_ERROR_CHECK( nvs_flash_init() );

    // initialise_wifi();

    int timeout = wifi_info->wifi_timeout;
    if(wifi_info->wifi_timeout < 10000)
        timeout = 10000;

    return wifi_join(wifi_info->ssid,wifi_info->password,timeout);
}
